/**
 *  JSimProd - Java simulation code for production processes
 *  Copyleft Jorge Mira Yagüe <jorge.mira.yague@gmail.com>
 *  Universidad de Alicante 2009
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package lib.simul;

import lib.exceptions.InvalidLengthException;
import lib.exceptions.OverlappedIntervalException;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import lib.exceptions.NonExistentStationException;
import lib.model.FactoryModel;
import lib.model.Interval;
import lib.plan.ProductionPlan;
import lib.xml.XMLizable;

/**
 * The Simulation class perform a specified number of simulations. It can stop 
 * or pause the simulation after an specified time. The class can make a any
 * number of agendas that may be very differents dependig of the parameters
 * their tasks.
 * @see Run
 * @see Agenda
 * @author Jorge Mira Yagüe
 */
public class Simulation implements XMLizable {

    private int maxIterations;
    private long maxTime;
    private int iteration;
    private FactoryModel factoryModel;
    private ProductionPlan productionPlan;
    private Vector<Run> runs;
    private boolean pause;
    private boolean stopped;
    private boolean timedOut;
    private boolean finished;
    private Timer timer;
    private TimerTask checkTime;
    private long startTimer;
    private long remainingTime;
    private Interval interval;

    /**
     * Builds a simulation with the given parameters
     * @param maxIterations the number of simulations that will be done
     * @param maxTime the maximum time in milliseconds that simulation may take
     * @param interval the time interval when the events will happen
     */
    public Simulation(int maxIterations, long maxTime, Interval interval)
            throws InvalidLengthException {
        this.maxIterations = maxIterations;
        this.maxTime = maxTime;
        this.interval = interval;

        if (maxTime <= 0 || maxIterations <= 0) {
            throw new InvalidLengthException();
        }

        iteration = 0;
        remainingTime = -1;
        pause = false;
        finished = false;
        stopped = false;
        timedOut = false;
        timer = new Timer(false);
        if (maxTime > 0) {
            checkTime = new TimerTask() {

                @Override
                public void run() {
                    if (!isFinished()) {
                        Pause();
                        timedOut = true;
                    }
                }
            };
        }
    }

    /**
     * Starts the simulation with the given parameters
     * @param factoryModel the factory where the simulation will be done
     * @param productionPlan the production plan needed to do the simulation
     */
    public void Start(FactoryModel factoryModel,
            ProductionPlan productionPlan)
            throws InvalidLengthException,
            OverlappedIntervalException,
            NonExistentStationException {
        Run element;

        pause = false;
        finished = false;
        timedOut = false;

        this.factoryModel = factoryModel;
        this.productionPlan = productionPlan;
        runs = new Vector<Run>(maxIterations);
        startTimer = new Date().getTime();
        timer.schedule(checkTime,
                maxTime,
                remainingTime == -1 ? maxTime : remainingTime);
        if (stopped) {
            iteration = 0;
        }

        while (iteration < maxIterations && !pause) {
            element = new Run(factoryModel);
            runs.add(element);
            element.CreateAgenda(productionPlan, interval);
            iteration++;
        }

        finished = true;
        timer.cancel();
    }

    /**
     * Stops the simulation without resume posibility
     */
    public void Stop() {
        runs.get(iteration).Pause();
        stopped = true;
        timer.cancel();
        remainingTime = -1;
    }

    /**
     * Pauses the simulation allowing to resume it later
     */
    public void Pause() {
        runs.get(iteration).Pause();
        remainingTime = startTimer - new Date().getTime();
        if (remainingTime <= 0) {
            remainingTime = -1;
        }
        pause = true;
    }

    /**
     * Returns the agendas that have been made on the simulation
     * @return a Vector with the created agendas
     */
    public Vector<Agenda> getResults() {
        int finishedAgendas = this.isFinished() ? maxIterations : iteration - 1;
        Vector<Agenda> agendas = new Vector<Agenda>(finishedAgendas);

        for (int i = 0; i < finishedAgendas; i++) {
            agendas.add(runs.get(i).getAgenda());
        }

        return agendas;
    }

    public boolean isFinished() {
        return finished;
    }

    public boolean isStopped() {
        return stopped;
    }

    public void setStopped(boolean stopped) {
        this.stopped = stopped;
    }

    public boolean isTimedOut() {
        return timedOut;
    }

    public FactoryModel getFactoryModel() {
        return factoryModel;
    }

    public ProductionPlan getProductionPlan() {
        return productionPlan;
    }

    public Interval getInterval() {
        return interval;
    }

    public String toXML() {
        String result = "";
        result += "<Simulation ";
        result += "ppName=\"" + productionPlan.getName() + "\" ";
        result += "fmName=\"" + factoryModel.getName() + "\" ";
        result += ">\n";
        result += interval.toXML();
        if (runs != null) {
            for (Run r : runs) {
                result += r.toXML();
            }
        }
        result += "</Simulation>\n";
        return result;
    }
}
